﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfFootball.Parser;
using System.Threading;
using System.Windows.Media.Animation;
#endregion

namespace WpfFootball.UI
{
    /// <summary>
    /// This is a base view that should be used by a view in the application.
    /// This class provides basic functionallity to all views such as animations
    /// drag & drop functionallity and options to manipulate other view properties.
    /// </summary>
    public class BaseView : UserControl, IClosableView
    {
        #region Fields
        protected ViewKind viewKind;
        protected string teamName;
        protected bool initializing = false;
        protected bool isUncheckingOther = false;
        private double originalLeft;
        private double originalTop;
        private bool grabbing = false;
        private bool dragging = false;
        private int originalZIndex;

        protected double defaultViewHeight;
        protected double hiddenViewHeight = 25;
        protected bool isHidden = false;
        protected bool standAloneMode = false;

        private Point grabbingPoint;
        protected ViewSettings viewSettings;
        protected DataParser parser;
        protected IOManager ioManager;
        protected Thread autoUpdateThread;

        private DoubleAnimation hideAnimation = new DoubleAnimation();
        private DoubleAnimation rotationAnimation = new DoubleAnimation();
        private DoubleAnimation scaleAnimation = new DoubleAnimation();
        private TimeSpan fadeAnimationLength = new TimeSpan(0, 0, 0, 0, 300);
        private TimeSpan rotationAnimationTime = new TimeSpan(0, 0, 1);
        private TimeSpan scaleTime = new TimeSpan(0, 0, 0, 0, 900);

        private Point defaultTransformOrigin = new Point(0.5, 0.5);
        private RotateTransform defaultRotateTransform = new RotateTransform(0);
        private ScaleTransform defaultScaleTransform = new ScaleTransform(0, 0);
        #endregion

        #region Virtual Methods
        public virtual void RegisterEventHandlers()
        {
            this.PreviewMouseRightButtonDown += UserControl_PreviewMouseRightButtonDown;
            this.PreviewMouseMove += UserControl_PreviewMouseMove;
            this.PreviewMouseRightButtonUp += UserControl_PreviewMouseRightButtonUp;
        }

        public virtual void InitializeView()
        { }

        public virtual void ApplyViewSettings()
        { }

        public virtual void RevalidateContextMenu()
        { }

        public virtual void AutoUpdateThreadWorker()
        { }

        public virtual void RevalidateAutoUpdateThread()
        { }
        #endregion
        
        #region Auto Updating Handlers
        /// <summary>
        /// Initializes an auto-update thread for this view.
        /// </summary>
        public virtual void InitAutoUpdateThread()
        {
            autoUpdateThread = new Thread(AutoUpdateThreadWorker);
            autoUpdateThread.Name = Enum.GetName(typeof(ViewKind), viewKind).ToString();
            autoUpdateThread.Start();
        }

        /// <summary>
        /// Attempts to stop the auto-update thread of this view, if its alive.
        /// </summary>
        public virtual void AbortAutoUpdateThread()
        {
            if (autoUpdateThread != null && autoUpdateThread.IsAlive)
            {
                autoUpdateThread.Abort();
            }
        }
        #endregion

        #region Animations
        /// <summary>
        /// Attempts to rotate a control given a starting and ending angle.
        /// </summary>
        /// <param name="from">The initial angle, in degrees.</param>
        /// <param name="to">The final target angle, in degrees.</param>
        /// <param name="controlToRotate">The control to be rotated .</param>
        public void RotateControl(double from, double to, Control controlToRotate)
        {
            controlToRotate.RenderTransform = defaultRotateTransform;
            controlToRotate.RenderTransformOrigin = defaultTransformOrigin;

            rotationAnimation.From = from;
            rotationAnimation.To = to;
            rotationAnimation.Duration = rotationAnimationTime;
            rotationAnimation.RepeatBehavior = new RepeatBehavior(int.MaxValue);

            RotateTransform rotation = (RotateTransform)controlToRotate.RenderTransform;
            rotation.BeginAnimation(RotateTransform.AngleProperty, rotationAnimation);
        }

        /// <summary>
        /// Scales up this view until it reaches the default size.
        /// </summary>
        public void ScaleUpViewAnimation()
        {
            this.RenderTransform = defaultScaleTransform;
            this.RenderTransformOrigin = defaultTransformOrigin;

            scaleAnimation.From = 0;
            scaleAnimation.To = 1;
            scaleAnimation.Duration = scaleTime;

            ScaleTransform scaleTrans = this.RenderTransform as ScaleTransform;
            scaleTrans.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation);
            scaleTrans.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation);
        }

        /// <summary>
        /// Stops any current rotation animations on a control and restores 
        /// it to its default angle.
        /// </summary>
        /// <param name="controlToStop">The control to stop animating.</param>
        public void StopControlRotation(System.Windows.Controls.Control controlToStop)
        {
            RotateTransform rotation = (RotateTransform)controlToStop.RenderTransform;
            rotation.BeginAnimation(RotateTransform.AngleProperty, null);
            controlToStop.RenderTransform = defaultRotateTransform;
        }
        #endregion

        #region View Drag & Drop Handling
        /// <summary>
        /// Checks if a mouse-click coordinate(described as a Point) is inside the
        /// specified dragging area of this view.
        /// </summary>
        /// <param name="grabbingOrigin">The position where the mouse click was registered.</param>
        /// <returns>True if the drag origin point is inside the grabbing area, otherwise false.</returns>
        private bool IsInGrabbingArea(Point grabbingOrigin)
        {
            Vector offset = VisualTreeHelper.GetOffset(this);
            return grabbingOrigin.Y > offset.Y && grabbingOrigin.X > (offset.X);
        }

        /// <summary>
        /// Translates the view so it always follows the current mouse position.
        /// </summary>
        private void DragMoved()
        {
            Point currentPosition = System.Windows.Input.Mouse.GetPosition(this.Tag as Canvas);

            double elementLeft = (currentPosition.X - grabbingPoint.X) + originalLeft;
            double elementTop = (currentPosition.Y - grabbingPoint.Y) + originalTop;
            Canvas.SetLeft(this, elementLeft);
            Canvas.SetTop(this, elementTop);
        }

        /// <summary>
        /// Does some prework before the view is able to be dragged.
        /// </summary>
        private void DragStarted()
        {
            dragging = true;
            originalLeft = Canvas.GetLeft(this);
            originalTop = Canvas.GetTop(this);
            originalZIndex = Canvas.GetZIndex(this);
            Canvas.SetZIndex(this, 90);
            this.CaptureMouse();
            this.Cursor = Cursors.ScrollAll;
            this.Opacity = 0.6;
        }

        /// <summary>
        /// Handles the PreviewMouseLeftButtonDown event, triggered when the user is clicking
        /// inside the view. If the mouse click is registered inside a grabbing area, the view will
        /// be prepared to be dragged.
        /// </summary>
        private void UserControl_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            grabbingPoint = e.GetPosition(this.Tag as Canvas);

            if (!standAloneMode && IsInGrabbingArea(grabbingPoint))
            {
                grabbing = true;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Sets the location of this view based on the current settings for this view.
        /// </summary>
        public virtual void SetViewLocation()
        {
            Canvas.SetLeft(this, viewSettings.Location.X);
            Canvas.SetTop(this, viewSettings.Location.Y);
        }

        /// <summary>
        /// NOtifies the underlayinng IOManager to stores the current location of this view.
        /// </summary>
        public virtual void CommitViewLocation()
        {
            Point currentLocation = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
            viewSettings.Location = currentLocation;
            ioManager.WriteViewSettings(teamName, viewKind, viewSettings);
        }

        /// <summary>
        /// Handles the PreviewMouseLeftButtonUp event, triggered when the user releases the left mouse
        /// button. If a drag operation was active it will be inmediatly aborted.
        /// </summary>
        private void UserControl_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (grabbing)
            {
                e.Handled = false;
                grabbing = false;
                dragging = false;

                this.ReleaseMouseCapture();
                this.Cursor = Cursors.Arrow;
                this.Opacity = 1;
                Canvas.SetZIndex(this, originalZIndex);
                CommitViewLocation();
            }
        }

        /// <summary>
        /// Handles the view dragging operations.
        /// </summary>
        private void UserControl_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (grabbing)
            {
                Rect parentRect = GetParentRect();

                if (!dragging && ViewDraggedMinimumDistance(e.GetPosition(this)))
                {
                    DragStarted();
                }
                else
                {
                    if (parentRect.Contains(e.GetPosition(this.Parent as Canvas)))
                    {
                        DragMoved();
                    }
                }
            }
        }
        #endregion

        #region Helpers
        /// <summary>
        /// Checks if the mouse pointer moved(while dragging) a minimum distance before
        /// attempting to move the view.
        /// </summary>
        /// <param name="currentMousePoint">The current position of the mouse pointer, described as a Point.</param>
        /// <returns>
        /// True if the mouse has moved a specified number of pixels, in this case a dragging operation can be done.
        /// False otherwise.
        /// </returns>
        private bool ViewDraggedMinimumDistance(Point currentMousePoint)
        {
            return Math.Abs(currentMousePoint.X - grabbingPoint.X) > SystemParameters.MinimumHorizontalDragDistance &&
                    Math.Abs(currentMousePoint.Y - grabbingPoint.Y) > SystemParameters.MinimumVerticalDragDistance;
        }

        /// <summary>
        /// Calculates the rectangle of the parent of this view.
        /// </summary>
        /// <returns>A Rect object that spcifies the area of the parent of this view.</returns>
        private Rect GetParentRect()
        {
            Canvas canvas = this.Parent as Canvas;
            Vector vect = VisualTreeHelper.GetOffset(canvas);

            return new Rect(vect.X, vect.Y, canvas.ActualWidth, canvas.ActualHeight);
        }
        #endregion

        #region Control Hide Handling
        /// <summary>
        /// Handles the view hiding functionality of this view. Depending on the
        /// current hiding state of the control, the view will be expanded or 
        /// retracted.
        /// </summary>
        protected virtual void hideButton_Click(object sender, RoutedEventArgs e)
        {
            if (!isHidden)
            {
                isHidden = true;
                hideAnimation.From = this.Height;
                hideAnimation.To = hiddenViewHeight;
                hideAnimation.Duration = fadeAnimationLength;
                this.BeginAnimation(System.Windows.Controls.UserControl.HeightProperty, hideAnimation);
            }
            else
            {
                isHidden = false;
                hideAnimation.From = this.Height;
                hideAnimation.To = defaultViewHeight;
                hideAnimation.Duration = fadeAnimationLength;
                this.BeginAnimation(System.Windows.Controls.UserControl.HeightProperty, hideAnimation);
            }
        }
        #endregion

        #region IClosableView Members
        public void CloseView()
        {
            AbortAutoUpdateThread();
        }
        #endregion

        protected void MenuItemCheckedHandler(object sender, RoutedEventArgs e)
        {
            MenuItem checkedItem = sender as MenuItem;
            MenuItem checkedItemParent = checkedItem.Parent as MenuItem;

            if (!initializing && !isUncheckingOther)
            {
                if (checkedItemParent == null)
                {
                    viewSettings.AutoUpdate = checkedItem.IsChecked;
                    RevalidateAutoUpdateThread();
                }
                else if (checkedItemParent.Name.Equals("updateFrequenceMenuItem"))
                {
                    viewSettings.UpdateFrequency = int.Parse(checkedItem.Tag.ToString());

                }
                else if (checkedItemParent.Name.Equals("entryCountMenuItem"))
                {
                    viewSettings.EntryCounter = int.Parse(checkedItem.Tag.ToString());
                }

                ioManager.WriteViewSettings(teamName, viewKind, viewSettings);
            }
        }

        /// <summary>
        /// Unchecks all menu items that belongs to the parent of a checked menu item, 
        /// excluding the newly checked item.
        /// </summary>
        /// <param name="checkedMenuItem">The menu item that recently has been checked.</param>
        /// <param name="checkedItemParent">The parent of the recently checked menu item.</param>
        /// MenuItem_Click(object sender, RoutedEventArgs e)
        protected void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem checkedMenuItem = sender as MenuItem;
            MenuItem checkedItemParent = checkedMenuItem.Parent as MenuItem;

            isUncheckingOther = true;

            foreach (dynamic currentMenuItem in checkedItemParent.Items)
            {
                if (currentMenuItem != checkedMenuItem)
                {
                    currentMenuItem.IsChecked = false;
                }
            }

            isUncheckingOther = false;
        }

    }
}
